// 封装判断是否为对象的方法
const isObject = val => val !== null && typeof val === 'object'
// 判断传入参数是否为对象,如果是对象,嵌套调用reactive函数,否则直接返回传入参数
const convert = target => isObject(target) ? reactive(target) : target
// 简写JS的hasOwnProperty方法
const hasOwnProperty = Object.prototype.hasOwnProperty
// 接收两个参数 返回传入的对象中是否存在某个属性
const hasOwn = (target, key) => hasOwnProperty.call(target, key)

// 首先判断传入的参数是不是对象
export function reactive(target) {
  // 如果target不是对象,那么就直接返回
  if(!isObject(target)) return target

  // 设置get/set/deleteProperty
  const handler = {
    get(target, key, receiver) {
      // 收集依赖   1.目标对象  2.属性
      track(target,key)
      // 获取值,如果key的值还是一个对象,那么需要递归处理
      const result = Reflect.get(target, key, receiver)
      return convert(result)
    },
    set(target, key, value, receiver){
      // 获取到修改之前的值
      const oldValue = Reflect.get(target, key, receiver)
      let result = true
      // 对比新旧两个值是否相同,如果不同进行修改
      if(oldValue !== value) {
        result = Reflect.set(target, key, value, receiver)
        // 触发更新
        trigger(target, key)
      }
      return result
    },
    deleteProperty(target, key) {
      // 判断target中是否有自己的key属性
      const hadKey = hasOwn(target, key)
      // 删除target中的key属性，并返回成功或失败
      const result = Reflect.deleteProperty(target, key)
      // 如果有key属性,并且删除成功
      if(hadKey && result) {
        // 触发更新
        trigger(target, key)
      }
      return result
    }
  }
  return new Proxy(target, handler)
}


// 当前获得的effect函数
let activeEffect = null

// 接收一个函数作为参数
export function effect(callback) {
  activeEffect = callback
  // 首先执行一次, 访问响应式对象属性,去收集依赖
  callback()
  activeEffect = null
}

let targetMap = new WeakMap()
// 收集依赖   接收两个参数, 1.目标对象  2.跟踪的属性key
export function track(target, key) {
  // 如果没有要收集的依赖,直接返回
  if(!activeEffect) return
  // 当前的target是targetMap中的键
  let depsMap = targetMap.get(target)
  if(!depsMap) {
    // 创建一个新的depsMap并添加到targetMap中
    targetMap.set(target, (depsMap = new Map()))
  }
  // 根据属性查找对应dep对象
  let dep = depsMap.get(key)
  if(!dep) {
    depsMap.set(key, (dep = new Set()))
  }
  // effect中的回到函数添加到dep集合中
  dep.add(activeEffect)
}

// 触发更新
export function trigger(target, key) {
  // 存储的是属性和dep集合
  const depsMap = targetMap.get(target)
  if(!depsMap) return
  // 根据key 找dep集合. 存储的就是这个key对应的effect函数
  const dep = depsMap.get(key)
  if(dep) {
    // 遍历dep集合, 执行这里所有的effect函数
    dep.forEach(effect => {
      effect()
    });
  } 
}

// ref函数的原理
export function ref(raw) {
  // 首先判断raw是不是使用ref创建的对象, 如果是则直接返回
  if(isObject(raw) && raw.__v_isRef) {
    return
  }
  // 判断raw是不是对象
  let value = convert(raw)
  const r = {
    __v_isRef: true, //标识符是否是ref对象
    get value() {
      // 收集依赖
      track(r, 'value') 
      return value
    },
    set value(newValue) {
      // 新旧值不相等时
      if(newValue !== value) {
        // 更新最新的值
        raw = newValue
        // 如果重新赋值是一个对象的话那么依然是响应式的，因为convert内部调用了reactive（ref和reactive的区别之一）
        value = convert(raw)
        // 触发更新
        trigger(r, 'value')
      }
    }
  }
  // 将ref对象返回
  return r
}

// 处理reactive返回的所有属性转换为对象(解构是按照引用传递的,所以解构后的值依然是响应式对象)
export function toRefs(proxy) {
  // 根据传入的响应式对象类型来创建一个新的ret 数组/对象
  const ret = proxy instanceof Array ? new Array(proxy.length) : {}
  // 遍历proxy将值处理后放入ret中
  for(const key in proxy) {
    ret[key] = toProxyRef(proxy, key)
  }
  // 返回ret
  return ret
}

// 转换proxy对象内值的函数
function toProxyRef(proxy, key) {
  const r = {
    __v_isRef: true, //标识符和ref一样
    // 这里不需要收集依赖,因为接收的proxy参数就是经过reactive处理过的
    get value() {
      // 这一步已经执行了收集依赖
      return proxy[key]
    },
    // 同样的这里不需要触发更新
    set value(newValue) {
      proxy[key] = newValue
    }
  }
  // 将处理后的对象返回
  return r
}

// 计算属性基本原理
export function computed(getter) {
  // ref函数不传参时默认传入undefined,那么属性value值就是undefined
  const result = ref()

  // 直接调用effect函数,执行赋值,将getter函数的返回值赋值给result.value
  effect(() => result.value = getter())

  return result
}
